#include "Character.h"
#include <typeinfo>
#include <map>

equipped::equipped()
{
	weapon = NULL;
	armor = NULL;
	other1 = NULL;
	other2 = NULL;
	other3 = NULL;
}

Weapon* equipped::defaultWeapon()
{
	return NULL;
}

Armor* equipped::defaultArmor()
{
	return NULL;
}

OtherItem* equipped::defaultOtherItem()
{
	return NULL;
}

Character::Character(std::string c_name, std::string race, bool is_male, Progress::ach killAch , int voice_pitch, int level, int exp_since_level_up, bool hostile):
name(c_name),
race(race),
kill(killAch),
level(0),
male(is_male),
pitch(voice_pitch),
hostile(hostile)
{
	exp = (level-1) * EXP_POINTS_PER_LEVEL + exp_since_level_up;
	prog = new Progress;
	defaultTrashTalk();
}

Character::~Character()
{
	delete prog;
	if(equipment.weapon != NULL){
		delete equipment.weapon;
		equipment.weapon = NULL;
	}
	if(equipment.armor != NULL){
		delete equipment.armor;
	}
	if(equipment.other1 != NULL){
		delete equipment.other1;
	}
	if(equipment.other2 != NULL){
		delete equipment.other2;
	}
	if(equipment.other3 != NULL){
		delete equipment.other3;
	}
	
}

void Character::defaultTrashTalk(){
	
defInsults["This is the END for you, you gutter-crawling cur!"] = "And I have got a little TIP for you, get the POINT?";
defInsults["Soon you will be wearing my sword like a shish kebab!"] = "First you better stop waiving it like a feather-duster.";
defInsults["My handkerchief will wipe up your blood!"] = "So you got that job as janitor, after all.";
defInsults["People fall at my feet when they see me coming."] = "Even BEFORE they smell your breath?";
defInsults["I once owned a dog that was smarter then you."] = "He must have taught you everything you know.";
defInsults["You make me want to puke."] = "You make me think somebody already did.";
defInsults["Nobody has ever drawn blood from me and no body ever will."] = "You run THAT fast?";
defInsults["You fight like a dairy farmer."] = "How appropriate. You fight like a cow.";
defInsults["I got this scar on my face during a mighty struggle!"] = "I hope now you have learned to stop picking your nose.";
defInsults["Have you stopped wearing diapers yet?"] = "Why, did you want to borrow one?";
defInsults["I have heard you were a contemptible sneak."] = "Too bad no one is ever heard of YOU at all.";
defInsults["You are no match for my brains, you poor fool."] = "I would be in real trouble if you ever used them.";
defInsults["You have the manners of a begger."] = "I wanted to make sure you would feel comfortable with me.";
defInsults["I am not going to take your insolence sitting down!"] = "Your hemorrhoids are flaring up again, eh?";
defInsults["There are no words for how disgusting you are."] = "Yes there are. You just never learned them.";
defInsults["I have spoken with apes more polite then you."] = "I am glad to hear you attended your family reunion.";
defInsults["This is the END for you, you gutter-crawling cur!"] = "And I have got a little TIP for you, get the POINT?";
defInsults["Every enemy I've met I've annihilated!"] = "With your breath, I'm sure they all suffocated.";
defInsults["You're as repulsive as a monkey in a negligee."] = "I look THAT much like your fiancée?";
defInsults["Would you like to be buried or cremated?"] = "With you around, I'd prefer to be fumigated.";
defInsults["Heaven preserve me! You look like something that's died!"] = "The only way you'll be preserved is in formaldehyde.";
defInsults["I'll skewer you, like a sow at a buffet."] = "When I'm done with YOU, you'll be a boneless fillet!";
defInsults["Killing you would be justifiable homicide."] = "Then killing you must be justifiable fungicide.";
defInsults["En garde! Touché!"] = "Oh, that is so cliché!";
defInsults["Throughout the Caribbean my great deeds are celebrated!"] = "Too bad they're all fabricated.";
defInsults["When your father first saw you, he must have been mortified."] = "At least mine can be identified.";
defInsults["You can't match my witty repartee."] = "I could, if you would use some breath spray.";
defInsults["I can't rest until you've been exterminated!"] = "Then perhaps you should switch to decaffeinated.";
defInsults["You're the ugliest monster ever created."] = "If you don't count all the ones you've dated.";
defInsults["I'll leave you devastated, mutilated and perforated."] = "Your odor alone makes me aggravated, agitated, and infuriated!";
defInsults["Coming face to face with me must leave you petrified."] = "Is that your face? I thought it was your backside!";
defInsults["I'll hound you night and day!"] = "Then be a good dog. Sit! Stay!";
}

std::string Character::getTrashTalk(std::string insult){
		if(insult.compare("") == 0){
			int r = rand() % defInsults.size();
			std::map<std::string,std::string>::iterator it = defInsults.begin();
			for(int i = 0; i < r;i++){
				it++;
			}
			return (*it).first;
		}
		if(defInsults[insult].compare("") == 0){
			std::vector<std::string> unknown = {"Boy, are you ugly!","What an idiot!","You call yourself a pirate?","I am rubber, you are glue. "};
			return unknown[rand() % unknown.size()];
		}
		else{
			return defInsults[insult];
		}
}

void Character::talk(Character * player){
	for(size_t i = 0; i < dialogs.size(); i++){
		dialog d = dialogs[i];
		bool correct = true;
		for(size_t j = 0; j < d.required.size(); j++){
			if(d.required[j].modifier != player->achieved(d.required[j].p)){
				correct = false;
				break;
			}
		}
		if(correct)
		{
			speak(d.dia, this);
			if(d.achievement.p != NONE){
				player->setAchieved(d.achievement);
			}
			
			return;
		}
	}
}



void Character::printEquipment(){
	int i = 0;
	if(equipment.weapon != NULL){
		i++;
		equipment.weapon->printInfo("weapon: ");
	}
	if(equipment.armor != NULL){
		i++;
		equipment.armor->printInfo("armor: ");
	}
	if(equipment.other1 != NULL){
		i++;
		equipment.other1->printInfo("item1: ");
	}
	if(equipment.other2 != NULL){
		i++;
		equipment.other2->printInfo("item2: ");
	}
	if(equipment.other3 != NULL){
		i++;
		equipment.other3->printInfo("item3: ");
	}
	if(i == 0){
		std::string say = name ;
		say += " has no equipment.";
		speak(say);
	}
}

void Character::defend(int dmg, bool mag)
{	
	int defend;
	if(mag)
	{
		defend = getMagDef() + (((rand()%(getMagDef()*2)) - getMagDef()) % getMagDef()/4);
	}
	else
	{
		defend = getDef() + (((rand()%(getDef()*2)) - getDef()) % getDef()/4);
	}
	
	std::string say = name;
	say += " took ";
	if((dmg - defend)  >= 0){
		addHP(-(dmg - defend));
		say += std::to_string(dmg - defend);
	}
	else{
		say += std::to_string(0);
	}
	say += " points in damage.";
	speak(say);
	say = name;
	say += " now have ";
	say += std::to_string(getHP());
	say+= " health points.";
	speak(say);
}

bool Character::battle(Character * player, bool playerAttack){ //return true = run away
	std::string say = "You are in battle with ";
	say += name;
	say+= ".";
	speak(say);
	if(playerAttack){
		std::string insult = player->getTrashTalk();
		speak(insult, player);
		speak(getTrashTalk(insult), this);
	}
	else{
		std::string insult = getTrashTalk();
		speak(insult, this);
		speak(player->getTrashTalk(insult), player);
	}
	
	while(true){
		if(playerAttack){
			std::vector<std::string> input = read(player->get_Battle_alternatives(), "What do you want to do (help for options)");
			if(input.size() == 1){
				if(input[0].compare("help") == 0){
					printAlternatives(player->get_Battle_alternatives());
					continue;
				}
				if(input[0].compare("attack") == 0){
					int attack = player->doAttack();
					if(attack == -1){
						continue;
					}
					defend(attack);
				}
				
			}
			if(input.size() == 2){
				if(input[0].compare("collect") == 0){
					if(input[1].compare("mana") == 0){
						player->gather(true);
					}
					if(input[1].compare("stamina") == 0){
						player->gather(false);
					}
				}
				if(input[0].compare("attack") == 0){
					if(input[1].compare("magic") == 0){
						int attack = player->doAttack(true);
						if(attack == -1){
							continue;
						}
						defend(attack);
					}
					if(input[1].compare("weapon") == 0){
						int attack = player->doAttack(false);
						if(attack == -1){
							continue;
						}
						defend(attack);
					}
				}
				if(input[0].compare("run") == 0){
					if(input[1].compare("away") == 0){
						return true;
					}
				}
				if(input[0].compare("check") == 0){
					if(input[1].compare("status") == 0){
						player->getStatus();
						continue;
					}
					if(input[1].compare("enemy") == 0){
						getStatus();
						continue;
					}
				}
			}
			if(getHP() == 0){
				speak("you win");
				player->setAchieved(kill);
				return false;
			}
			playerAttack = false;
		}
		else{
			int type = attackPossible();
			bool attack = false;
			if(type == 1){
				attack = true;
			}
			if(type == 2){
				attack = false;
			}
			if(type != 3){
				int dmg = doAttack(attack);
				player->defend(dmg, attack);
			}
			else{
				gather(rand() % 1);
			}
			if(player->getHP() == 0){
				speak("you die");
				return false;
			}
			playerAttack=true;
		}
	}
}

int Character::getHP(){
	return HP;
}

void Character::addDef(int change){
	def += change;
}

int Character::getMagDef(){
	return magDef;
}

void Character::addHP(int change){
	if(HP + change > getMaxHP()){
		HP = getMaxHP();
	}
	else if(HP + change < 0){
		HP = 0;
	}
	else{
		HP += change;
	}
}

void Character::addMaxHP(int change){
	maxHP += change;
}

int Character::getMaxHP(){
	return maxHP + getBonus(4);
}

int Character::getBonus(int attr){ //[atk, def, mag, maxMana, maxHP, Stamina, magDef]
	int bonus = 0;
	if(attr >= 0 && attr < 7) // TODO ugly way to check this... should maybe fix... or not
	{
		if(equipment.weapon != NULL){
			bonus += equipment.weapon->getModifiers()[attr];
		}
		if(equipment.armor != NULL){
			bonus += equipment.armor->getModifiers()[attr];
		}
		if(equipment.other1 != NULL){
			bonus += equipment.other1->getModifiers()[attr];
		}
		if(equipment.other2 != NULL){
			bonus += equipment.other2->getModifiers()[attr];
		}
		if(equipment.other3 != NULL){
			bonus += equipment.other3->getModifiers()[attr];
		}
	}
	
	return bonus;
}


int Character::getexp(){
	return exp;
}

int Character::getDef(){
	return def + getBonus(1);
}

bool Character::getGender(){
	return male;
}

int Character::getPitch(){
	return pitch;
}

void Character::addExperience(int change, bool dialog){
	if(level == 0)
	{
		levelUp();
	}
	if(change < 0)
	{
		printf("Losing exp is unfair!");
		return;
	}
	if(change == 0)
	{
		return;
	}
	
	exp += change;
	std::string say = "Wohoo I got ";
	say += std::to_string(change);
	say += " experience (total ";
	say += std::to_string(exp);
	say += ")";
	int oldlevel = level;
	while(exp >= (level * EXP_POINTS_PER_LEVEL))
	{
		levelUp();
	}
	if(level > oldlevel)
	{
		say += " and reached level ";
		say += std::to_string(level);
	}
	if(dialog) speak(say, this);
}

Weapon*  Character::getEquippedWeapon(){
	
	return equipment.weapon;
}

bool Character::equipItem(Item& item, bool dialog)
{
	try
	{
		OtherItem & otheritem = dynamic_cast<OtherItem&>(item);
		return equipItem(otheritem, dialog);
	}
	catch(std::bad_cast exp)
	{
	}
	try
	{
		Weapon & weapon = dynamic_cast<Weapon&>(item);
		return equipItem(weapon, dialog);
	}
	catch(std::bad_cast exp)
	{
	}
	try
	{
		Armor & armor = dynamic_cast<Armor&>(item);
		return equipItem(armor, dialog);
	}
	catch(std::bad_cast exp)
	{
	}
	return false;
}

std::vector<cmdalt> Character::get_equip_alternatives(Item& item1, Item& item2){
	std::vector<cmdalt> alternatives;
	{
		// help
		cmdalt alt;
		alt.first = "help";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// yes
		cmdalt alt;
		alt.first = "yes";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// no
		cmdalt alt;
		alt.first = "no";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// inspect
		cmdalt alt;
		alt.first = "inspect";
		alt.second = {item1.getName(), item2.getName()};
		alternatives.push_back(alt);
	}
	
	return alternatives;
}

bool Character::changeEquipment(Item& oldItem, Item& newItem){	
	while(true){
		std::string say = "Do you want to discard ";
		say += oldItem.getName();
		say += " and equip ";
		say += newItem.getName();
		say += " instead? (help for options)";
		std::vector<std::string> input = read(get_equip_alternatives(oldItem, newItem), say);
		if(input.size() == 1){
			if(input[0].compare("help") == 0){
				printAlternatives(get_equip_alternatives(oldItem, newItem));
			}
			if(input[0].compare("yes") == 0){
				say = oldItem.getName();
				say += " was destroyed and ";
				say += newItem.getName();
				say += " was equipped instead";
				speak(say);
				return true;
			}
			if(input[0].compare("no") == 0){
				return false;
			}
		}
		if(input.size() == 2){
			if(input[0].compare("inspect") == 0){
				if(input[1].compare(oldItem.getName()) == 0){
					oldItem.printInfo();
				}
				if(input[1].compare(newItem.getName()) == 0){
					newItem.printInfo();
				}
			}
		}
	}
}

bool Character::equipItem(Weapon& item, bool dialog)
{
	if(equippable(item))
	{
		if(equipment.weapon != NULL){
			if(!dialog || changeEquipment(*equipment.weapon, item)){
				delete equipment.weapon;
				equipment.weapon = &item;
				return true;
			}
			else{
				return false;
			}
		}
		else{
			equipment.weapon = &item;
			if(dialog){
				std::string say = "You equipped ";
				say += item.getName();
				say += ".";
				speak(say);
			}
			return true;
		}
	}
	delete &item;
	return false;
}

bool Character::equipItem(Armor& item, bool dialog)
{
	if(equippable(item))
	{
		if(equipment.armor != NULL){
			if(!dialog || changeEquipment(*equipment.armor, item)){
				delete equipment.armor;
				equipment.armor = &item;
				return true;
			}
		}
		else{
			equipment.armor = &item;
			if(dialog){
				std::string say = "You equipped ";
				say += item.getName();
				say += ".";
				speak(say);
			}
			return true;
		}
	}
	delete &item;
	return false;
}

std::vector<cmdalt> Character::get_deequip_alternatives(){
	std::vector<cmdalt> alternatives;
	{
		// help
		cmdalt alt;
		alt.first = "help";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// cancel
		cmdalt alt;
		alt.first = "cancel";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// check item
		cmdalt alt;
		alt.first = "check";
		alt.second = {"items"};
		alternatives.push_back(alt);
	}
	{
		// discard [item]
		cmdalt alt;
		alt.first = "discard";
		std::vector<std::string> sec;
		if(equipment.other1 != NULL){
			sec.push_back("item1");
		}
		if(equipment.other2 != NULL){
			sec.push_back("item2");
		}
		if(equipment.other3 != NULL){
			sec.push_back("item3");
		}
		alt.second = sec;
		if(sec.size() > 0){
			alternatives.push_back(alt);
		}
	}
	return alternatives;
}


bool Character::isHostile()
{
	return hostile;
}

bool Character::equipItem(OtherItem& item, bool dialog)
{	
	if(equippable(item))
	{
		// TODO as the others ask if player wants to equip... remember dialog = false means that we should equip
		if(equipment.other1 == NULL)
		{
			equipment.other1 = &item;
			if(dialog){
				std::string say =  "You equipped ";
				say += item.getName();
				say += ".";
				speak(say);
			}
			
			return true;
		}
		else if(equipment.other2 == NULL)
		{
			equipment.other2 = &item;
			if(dialog){
				std::string say =  "You equipped ";
				say += item.getName();
				say += ".";
				speak(say);
			}
			return true;
		}
		else if(equipment.other3 == NULL)
		{
			equipment.other3 = &item;
			if(dialog){
				std::string say =  "You equipped ";
				say += item.getName();
				say += ".";
				speak(say);
			}
			return true;
		}
		else{
			
			while(true){
				std::vector<std::string> input = read(get_deequip_alternatives(), "What item do you want to discard. (help for options)");
				if(input[0].compare("help") == 0){
					printAlternatives(get_deequip_alternatives());
				}
				if(input[0].compare("cancel") == 0){
					//remove parameter item or Not??
					return false;
				}
				if(input[0].compare("check") == 0){
					if(input[1].compare("items") == 0){
						speak("You have these items:");
						if(equipment.other1 != NULL){
							equipment.other1->printInfo("item1:");
						}
						if(equipment.other2 != NULL){
							equipment.other2->printInfo("item2:");
						}
						if(equipment.other3 != NULL){
							equipment.other3->printInfo("item3:");
						}
						item.printInfo("New item:");
					}
				}
				if(input[0].compare("discard") == 0){
					std::string old_name;
					std::string new_name;
					if(input[1].compare("item1") == 0){
						old_name = equipment.other1->getName();
						delete equipment.other1;
						equipment.other1 = NULL;
						equipment.other1 = &item;
						new_name = equipment.other1->getName();
					}
					if(input[1].compare("item2") == 0){
						old_name = equipment.other2->getName();
						delete equipment.other2;
						equipment.other2 = NULL;
						equipment.other2 = &item;
						new_name = equipment.other2->getName();
					}
					if(input[1].compare("item3") == 0){
						old_name = equipment.other3->getName();
						delete equipment.other3;
						equipment.other3 = NULL;
						equipment.other3 = &item;
						new_name = equipment.other3->getName();
					}
					std::string say = "You have discarded ";
					say += old_name;
					say += " and equipped ";
					say += new_name;
					say += " instead.";
					speak(say);
					return true;
				}
			}
		}
	}
	return false;
}

bool Character::achieved(progress p)
{
	return prog->achieved(p);
}

void Character::setAchieved(Progress::ach p)
{
	if(p.p != NONE)
	{
		prog->addProgress(p.p);
	}
	addExperience(p.xp);
	if(p.item != NULL){
		bool accepted = equipItem(*p.item);
		if(accepted){
			std::string say = "I will put this to good use.";
			speak(say, this);
		}
		else{
			speak("I don't want that!", this);
		}
	}
}

void Character::setDialogs(std::vector<dialog> d)
{
	dialogs = d;
}

void Character::setTrashTalk(std::vector<std::string> trash){
	trashTalk = trash;
	std::vector<std::string> unknown = {"Boy, are you ugly!","What an idiot!","You call yourself a pirate?","I am rubber, you are glue. "};
	for(size_t i = 0; i < trash.size(); i++){
		defInsults[trash[i]] = unknown[rand() % unknown.size()];
	}
}

std::vector<cmdalt> Character::get_loot_alternatives(){
	std::vector<cmdalt> alternatives;
	{
		// help
		cmdalt alt;
		alt.first = "help";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// done
		cmdalt alt;
		alt.first = "done";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// check equipment
		cmdalt alt;
		alt.first = "check";
		alt.second = {"equipment"};
		alternatives.push_back(alt);
	}
	{
		// take [item]
		cmdalt alt;
		alt.first = "take";
		std::vector<std::string> sec;
		if(equipment.weapon != NULL){
			sec.push_back("weapon");
		}
		if(equipment.armor != NULL){
			sec.push_back("armor");
		}
		if(equipment.other1 != NULL){
			sec.push_back("item1");
		}
		if(equipment.other2 != NULL){
			sec.push_back("item2");
		}
		if(equipment.other3 != NULL){
			sec.push_back("item3");
		}
		alt.second = sec;
		if(sec.size() > 0){
			alternatives.push_back(alt);
		}
	}
	return alternatives;
}

void Character::loot(Character * player){
	while(true){
		std::vector<std::string> input = read(get_loot_alternatives(), "What do you want to loot. (help for options)");
		if(input.size() == 1){
			if(input[0].compare("help") == 0){
				printAlternatives(get_loot_alternatives());
			}
			if(input[0].compare("done") == 0){
				return;
			}
		}
		if(input.size() == 2){
			if(input[0].compare("check") == 0 && input[1].compare("equipment") == 0){
				int i = 0;
				if(equipment.weapon != NULL){
					i++;
					equipment.weapon->printInfo("weapon: ");
				}
				if(equipment.armor != NULL){
					i++;
					equipment.armor->printInfo("armor: ");
				}
				if(equipment.other1 != NULL){
					i++;
					equipment.other1->printInfo("item1: ");
				}
				if(equipment.other2 != NULL){
					i++;
					equipment.other2->printInfo("item2: ");
				}
				if(equipment.other3 != NULL){
					i++;
					equipment.other3->printInfo("item3: ");
				}
				if(i == 0){
					std::string say = name;
					say += " has no equipment.";
					speak(say);
				}
			}
			if(input[0].compare("take") == 0){
				if(input[1].compare("weapon") == 0){
					bool taken = player->equipItem(*equipment.weapon);
					if(taken){
						equipment.weapon = NULL; 
					}
					else{
						std::string say = "I will let ";
						say += name;
						say += " keep ";
						if(male){
							say += "his ";
						}
						else{
							say += "hers ";
						}
						say += "weapon.";
						speak(say, player);
					}
				}
				if(input[1].compare("armor") == 0){
					bool taken = player->equipItem(*equipment.armor);
					if(taken){
						equipment.armor = NULL;
					}
					else{
						std::string say = "I will let ";
						say += name;
						say += " keep ";
						if(male){
							say += "his ";
						}
						else{
							say += "hers ";
						}
						say += "armor.";
						speak(say, player);
					}
				}
				if(input[1].compare("item1") == 0){
					bool taken = player->equipItem(*equipment.other1);
					if(taken){
						equipment.other1 = NULL;
					}
					else{
						std::string say = "I will let ";
						say += name;
						say += " keep that.";
						speak(say, player);
					}
				}
				if(input[1].compare("item2") == 0){
					bool taken = player->equipItem(*equipment.other2);
					if(taken){
						equipment.other2 = NULL;
					}
					else{
						std::string say = "I will let ";
						say += name;
						say += " keep that.";
						speak(say, player);
					}
				}
				if(input[1].compare("item3") == 0){
					bool taken = player->equipItem(*equipment.other3);
					if(taken){
						equipment.other3 = NULL;
					}
					else{
						std::string say = "I will let ";
						say += name;
						say += " keep that.";
						speak(say, player);
					}
				}
			}
		}	
	}
}
